// VBMcgi.cpp
// www.VBMcgi.org

// let the define below if want the code of VBMcgi for version 2.3
#define POST23


#include "VBMcgi/VBMcgi.h"
#include "VBMcgi/VBMcgi_buildid.h"
#include <stdarg.h>
//using namespace br::com::sbVB::VBMcgi;
using namespace br::com::sbVB::VBLib;
using namespace std;


// defined inside the VBMcgi object, but used as a global variable due to the "static"
bool br::com::sbVB::VBMcgi::VBMcgi::m_static_haveHeader = false;

// default constructor
br::com::sbVB::VBMcgi::VBMcgi::VBMcgi() 
:	m_tok(','),	m_csecure(false)
{
	m_pcout = &cout;
	addFunction(file);
	addFunction(SSI);
}; 


// copy constructor
br::com::sbVB::VBMcgi::VBMcgi::VBMcgi(const VBMcgi & obj) 
{
	m_pcout = obj.m_pcout;
	m_tok = obj.m_tok;
	m_xchgList = obj.m_xchgList;
	m_xchgListGVC = obj.m_xchgListGVC;
	m_funcList = obj.m_funcList;
	m_csecure = false;
}

void br::com::sbVB::VBMcgi::VBMcgi::setOstream(ostream * pcout) {
	m_pcout = pcout;
}


void br::com::sbVB::VBMcgi::VBMcgi::operator=(const VBMcgi & obj) 
{
	m_pcout = obj.m_pcout;
	m_xchgList = obj.m_xchgList;
	m_xchgListGVC = obj.m_xchgListGVC;
	m_funcList = obj.m_funcList;
}

char br::com::sbVB::VBMcgi::VBMcgi::getTok() const
{
	return m_tok;
}

void br::com::sbVB::VBMcgi::VBMcgi::setTok(char tok)
{
	m_tok = tok;
}

void br::com::sbVB::VBMcgi::VBMcgi::removeBySource(const char* find) 
{
	VBMcgi_xchgNode f(find,""); // the second string is not used
	m_xchgList.remove(f);
}

br::com::sbVB::VBLib::VBString 
br::com::sbVB::VBMcgi::VBMcgi::getHtmlParameter(unsigned i)
{
	return m_htmlParameters.strtok(m_tok,i);
}



// add one pair for each call
void br::com::sbVB::VBMcgi::VBMcgi::addBySource
(const char* find, const char* replace) 
{
	if (strlen(find)) // if find string exists
	{ 
		VBMcgi_xchgNode xchgNodeLocal(find,replace);
		m_xchgList.add(xchgNodeLocal);
	}
};


// replace is 1 char
void br::com::sbVB::VBMcgi::VBMcgi::addBySource(const char* find, char replace)
{
	char replaceStr[2];
	replaceStr[0] = replace;
	replaceStr[1] = 0; // terminate string
	addBySource(find,replaceStr);
}


void br::com::sbVB::VBMcgi::VBMcgi::addBySource(const char* find, unsigned replace)
{
	VBString replaceStr;
	replaceStr += replace;
	addBySource(find,replaceStr);
}


void br::com::sbVB::VBMcgi::VBMcgi::addBySource(const char* find, int replace)
{
	VBString replaceStr;
	replaceStr += replace;
	addBySource(find,replaceStr);
}


void br::com::sbVB::VBMcgi::VBMcgi::addBySource(const char* find, double replace)
{
	VBString replaceStr;
	replaceStr += replace;
	addBySource(find,replaceStr);
}


void br::com::sbVB::VBMcgi::VBMcgi::addBySource(const char* find, float replace)
{
	VBString replaceStr;
	replaceStr += replace;
	addBySource(find,replaceStr);
}


br::com::sbVB::VBLib::VBString 
br::com::sbVB::VBMcgi::VBMcgi::getVarContent(const char *find) {
	// possible bug here, because use of replace in first argument of tempData
	// xchgNode xchgNodeLocal(find,find);
	VBMcgi_xchgNode *ptrTemp;  // =xchgList.get(xchgNodeLocal);

	// exchange strings like done in changeAndStdOut
	for (ptrTemp = m_xchgListGVC.GetFirst() ; ptrTemp ; 
	  ptrTemp = m_xchgListGVC.GetNext()) 
	{
		if (ptrTemp->m_find == VBString(find)) 
		{
			return (ptrTemp->m_replace);  // return the char* inside VBString
			// break; // break the search in the list
		}
	}
	return ("");  // no data to show
};



// overload getVarContent function to check if
// some var name and var content match
bool br::com::sbVB::VBMcgi::VBMcgi::getVarContent
  (const char *find, const char *replace, ...) {
	// check if find have at least one pair

	if (VBString(getVarContent(find)) == VBString("")) {
		return (false);
	}
	bool foundNode=false;
	char *varList = (char *) replace;
	VBMcgi_xchgNode *ptrTemp;

	// Initialize variable arguments.
	va_list marker;
	va_start( marker, replace );

	while (strlen(varList)) {
		const char *szVarList = varList;

		// for all variables registered 
		for (ptrTemp = m_xchgListGVC.GetFirst() ; (ptrTemp && !foundNode); 
		  ptrTemp = m_xchgListGVC.GetNext()) 
		{
			if (VBString(find) == ptrTemp->m_find  && 
				VBString(szVarList) == ptrTemp->m_replace ) 
			{
				foundNode = true;
			}
			else 
			{
				foundNode = false; 
			}
		}

		// get the next argument
		if (foundNode) {
			varList = va_arg( marker, char*);
			if (strlen(varList)) foundNode = false;
		}
		else varList=""; // terminate the while

	} // end while

	// Reset variable arguments.
	va_end( marker );

	return (foundNode);
}; 


bool br::com::sbVB::VBMcgi::VBMcgi::getCheckBox
  (const char *find,const  char *replace) 
{
	return getVarContent(find,replace,"");
}

void br::com::sbVB::VBMcgi::VBMcgi::httpCompleteHeader(const char *headerType)
{
	if (!m_static_haveHeader) 
	{
		httpHeader(headerType); 
		*m_pcout << endl; // finalize header
		m_static_haveHeader = true;
	}
}

void br::com::sbVB::VBMcgi::VBMcgi::out
  (const char* filein)
{
	// send html header *if* not sent yet
	httpCompleteHeader();

	// streambuf* pCoutSbuf = cout.rdbuf(); // saves a pointer to cout's default stream buffer
	// cout.rdbuf(myCout.rdbuf()); // cout now outputs to myCout

// START - new code block by Mauricio de Alencar 2002-08-06
	VBString inputLine;
	VBString callArgument;
// END - new code block by Mauricio de Alencar 2002-08-06

/* START - old code block < 2002-08-06
	VBString inputLine, stringCall;
** END - old code block < 2002-08-06 */

	VBString functionToCall;
	VBString stringAfterCall;
	VBMcgi_xchgNode *ptrTemp=0;

	// try to open input file
	// ifstream existFile(filein NOCREATEFILE);
	ifstream existFile(filein);
	bool fileExists = (existFile != 0);
	ifstream myInputFile;
	if (fileExists)
		// try open file for reading only if file does exist.
		// This test is necessary because in Windows (VC6), if the file
		// does not exist, an enpty file is created when you try to open it
		myInputFile.open(filein);

	if (!fileExists || !myInputFile) 
	{

		VBString tmpString(CANOTOPENFILE);
		tmpString += filein;
		// send to sdtout a error msg
		htmlError(tmpString);
	}

	// read the entire changeFile and keep its contents as VBStrings
	// in the linked list

	VBString callReservedWord = CALL_SIG; // most likely "VBMCGI_CALL"
	char tok[2]; // tok as string
	tok[0] = m_tok;
	tok[1] = 0; // terminate string
	callReservedWord += "(";

	while (true) // loop to scan the entire file
	{
		myInputFile >> inputLine;
		if (myInputFile.eof()) break;
		if (inputLine != VBString("")) // skip blank lines
		{ // if line not blank
			// implementation of string change feature
			for (ptrTemp = m_xchgList.GetFirst() ; ptrTemp ; 
			ptrTemp = m_xchgList.GetNext())
				inputLine.strschg(ptrTemp->m_find,ptrTemp->m_replace);

			// string change feature done for current line. 
			// Now implement the call function feature
			// START - new code block by Mauricio de Alencar 2002-08-06
			stringAfterCall=inputLine;

			// implement the call function feature
			// scan the current line to find callReservedWord
			// callReservedWord is most likely to be "VBMcgi_CALL"
			while (true) 
			{
				// send string before next call
				*m_pcout << stringAfterCall.beforeFind(callReservedWord);

				// store string after call and function name
				stringAfterCall = stringAfterCall.afterFind(callReservedWord);

				// if there was no more ocurrences of callReservedWord, break loop
				if (stringAfterCall == "") break;

				// get argument of CALL_SIG(argument); argument=name,parameters
				callArgument = stringAfterCall.beforeFind(")");

				// get function name
				functionToCall = callArgument.beforeFind(tok);

				// get function parameters
				m_htmlParameters = callArgument.afterFind(tok);

				if (m_htmlParameters == "")
				{
					m_numberOfParameters = 0;
				}
				else
				{
					m_numberOfParameters = m_htmlParameters.tokCount(m_tok) + 1;
				}

				// run specified function, if exist
				//if (functionToCall != VBString("")) // unnecessary
				runFunction(functionToCall);

				// store only string after call
				stringAfterCall = stringAfterCall.afterFind(")");

			};

			*m_pcout << endl;
			// END - new code block by Mauricio de Alencar 2002-08-06

		}
		else  // if line is blank
			*m_pcout << endl;

	} // end of loop to scan the entire file

	myInputFile.close();


	// START - new code block by Mauricio de Alencar 2002-09-09
	// cout.rdbuf(pCoutSbuf); // restores cout's default stream buffer
	// END - new code block by Mauricio de Alencar 2002-09-0
};


VBString br::com::sbVB::VBMcgi::VBMcgi::systemDatePortuguese() 
{
	// this code should be changed by class VBDateTime of VBLib
	VBString ret;
	time_t ltime;
	time (&ltime); // get time
	struct tm *t = localtime (&ltime); // convert to tm struct

        char *months[] = {
                "Janeiro",
                "Fevereiro",
                "Mar\xe7o",
                "Abril",
                "Maio",
                "Junho",
                "Julhy",
                "Agosto",
                "Setembro",
                "Outubro",
                "Novembro",
                "Dezembro"
        };

	char buffer[10];
	ret  = "";
	sprintf(buffer,"%02d",t->tm_mday);  ret += buffer;
	ret += " de ";
	ret += months[t->tm_mon];
	ret += " de ";
	ret += (1900 + t->tm_year);
	return ret;

};

VBString br::com::sbVB::VBMcgi::VBMcgi::systemTime() 
{
	// this code should be changed by class VBDateTime of VBLib
	VBString ret;
	time_t ltime;
	time (&ltime); // get time
	struct tm *t = localtime (&ltime); // convert to tm struct
	char buffer[10];
	ret = "";
	sprintf(buffer,"%02d",t->tm_hour);  ret += buffer;
	ret += ":";
	sprintf(buffer,"%02d",t->tm_min);  ret += buffer;\
	ret += ":";
	sprintf(buffer,"%02d",t->tm_sec);  ret += buffer;
	return ret;
};





// add pairs reading from file
// return 0 means alloc ok
// retunr 1 means could not alloc memory
bool br::com::sbVB::VBMcgi::VBMcgi::addByFile(const char* filein) {

	VBString vn,vc,tmpString;
	ifstream infile;
	infile.open(filein);
	if (!infile) {
		VBString tmpString(CANOTOPENFILE);
		tmpString += filein;
		// send to sdtout a error msg 
		htmlError(tmpString);
		return (1); // return error
	}

	// master loop to read file
	while (!infile.eof()) {
		infile >> tmpString; 
		vn = tmpString.beforeFind(" ");
		vc = tmpString.afterFind(" ");
		addBySource(vn,vc);
	};
	return(0);  // return success
};



// add pair reading fron file with special format
bool br::com::sbVB::VBMcgi::VBMcgi::addBySpecialFile(const char* filein) 
{
	VBString vbTemp;
	ifstream myChangeFile(filein);
	if (!myChangeFile) {
		VBString tmpString(CANOTOPENFILE);
		tmpString += filein;
		// send to sdtout a error msg 
		htmlError(tmpString);
		return (1); // return error
	}

	// read the entire changeFile and keep its contents as VBStrings
	// in the linked list
	while (!myChangeFile.eof()) {
		bool foundOK=false;
		myChangeFile >> vbTemp;
		if (vbTemp != VBString("")) {
			// char tok = vbTemp.operator[](0); // first letter of string.
			char tok = vbTemp[0]; 
			VBString vn = vbTemp.strtok(tok,1,foundOK);
			if (!foundOK) {
				htmlError("Error in strtok");
				return(1);
			}

			VBString vc = vbTemp.strtok(tok,2,foundOK);
			if (!foundOK) {
				htmlError("Error in strtok");
				return(1);
			}

			addBySource(vn,vc);
		}
	}

	myChangeFile.close();
	return (0);
}

// add one pair for each call  return 0 means alloc o
// retunr 1 means could not alloc memory
// put pairs of function name and pointers to functions
// in a linked list to future use from runFunction
// pToUserClass is ...
bool br::com::sbVB::VBMcgi::VBMcgi::addFunctionHelper 
(const char *functionName, fpType pointer, void *pToUserClass) 
{
	funcNode funcNodeLocal(functionName,pointer,pToUserClass);

	// test if have space to alloc data
	if (m_funcList.add(funcNodeLocal)) {
		htmlError(INTERNAL_ERROR);
		return (1);	
	};
	return(0);
}







// decode qyery string from browser
// return 0 if no error, or 1 if error.
int br::com::sbVB::VBMcgi::VBMcgi::formDecode(bool copyToGetVarContents) 
{
#ifdef POST23
	char *bufferDataIn=0, *bufferDataOut=0; 
#endif


	VBString queryStringStr;
	const char *requestMethod, *queryString; // , *contentType;
	VBString contentType;
	long contentLength=0;
	HttpMethod method;
	bool error=false;


	// define request method
	// POST method has CONTENT_TYPE and CONTENT_LENGTH and not QUERY_STRING
	// GET method has QUERY_STRING and not CONTENT_TYPE nor CONTENT_LENGTH
	requestMethod=getenv("REQUEST_METHOD");
	if (requestMethod == 0) 
	{
		// no REQUEST_METHOD specified, assume GET
		method=GET;
	} 
	else 
	{
		VBString requestMethodStr = requestMethod;
		requestMethodStr.toUpper();
		// define INPUT method : accept POST or GET and reject any other
		method = UNDEFINED;
		if (requestMethodStr == "GET") method=GET;
		if (requestMethodStr == "POST") method=POST;
	}

	// request method is defined. It should be either get or post
	switch (method) 
	{
		// this method passes data through environment variable QUERY_STRING
		case GET:  
		{ 
			queryString=getenv("QUERY_STRING");
			if (queryString != 0)
				// if there's something in the variable queryString
				queryStringStr = queryString;
			else
				// if there's nothing in the variable queryString
				queryStringStr = NO_QUERY_STRING;
			break;
		} // end of case GET

		// this method passes data through stdin
		case POST:  
		{ 
// code of version 2.3
#ifdef POST23

			// first verify CONTENT_TYPE
			// in future will call another function to operate with others data type
			if 	(strcmp(getenv("CONTENT_TYPE"),"application/x-www-form-urlencoded")) 
			{
				htmlError("VBMcgi Error: Data type not supported, must be application/x-www-form-urlencoded");
				return(1); // return error and stop program 
			}

			// next read data from STDIN
			// get size of data to read, put it in long variable
			contentLength = atol(getenv("CONTENT_LENGTH"));

			// create bufferDataIn and bufferDataOut 
			bufferDataIn = new char[(contentLength+1)]; // +1 due to string terminator 0
			if(!bufferDataIn)  {
				htmlError(INTERNAL_ERROR);
				return (1);
			}

			strncpy(bufferDataIn,"",contentLength+1);
			bufferDataOut = new char[(contentLength+1)]; // +1 due to string terminator 0
			if(!bufferDataOut) {
				htmlError(INTERNAL_ERROR);
				return (1);
			}

			strncpy(bufferDataOut,"",(contentLength+1));

			// read data from SDTIN and put it in bufferDataIn
			cin.read( bufferDataIn , contentLength );
			bufferDataIn[contentLength]='\0';

			// output error if size of data if not equal to contenLength 
			if (cin.gcount() != contentLength)
			{
				htmlError ("VBMcgi Error: I/O error reading url encoded string"); 
				return (1);
			}

			// added to compatibilize version 2.3
			queryStringStr = bufferDataIn;
			break;

#endif



// begin of POST code that does not support http upload
#if 0
			// first verify CONTENT_TYPE
			// in future will call another function to operate with others data type
			char *contType = getenv("CONTENT_TYPE");
			if 	(contType == 0 || VBString(contType) != "application/x-www-form-urlencoded") 
			{
				htmlError("VBMcgi Error: Data type not supported, must be application/x-www-form-urlencoded");
			}

			// get size of data to read, put it in long variable
			char *contentLengthStr = getenv("CONTENT_TYPE");
			contentLength = 0;
			if (contentLengthStr)
				contentLength = atol(contentLengthStr);

			// create buffer and bufferDataOut 
			char *buffer = new char[(contentLength+1)]; // +1 for the termination char
			if(!buffer) 
			{
				htmlError(INTERNAL_ERROR);
				return (1);
			}

			buffer[0] = 0; // terminate string

			// read data from SDTIN and put it in bufferDataIn
			cin.read(buffer,contentLength);
			buffer[contentLength]='\0';

			// output error if size of data if not equal to contenLength 
			if (cin.gcount() != contentLength)
				htmlError ("VBMcgi Error: I/O error reading query string"); 

			// copy to a VBString
			queryStringStr = buffer;

			// delete buffer
			if (buffer)
				delete [] buffer;
			break;
#endif
// end of POST code that does not support http upload



// POST code that supports http upload
#if 0
			// encoding is defined in the CONTENT_TYPE environment variable
			// encoding can be:
			// "application/x-www-form-urlencoded": just like GET method's QUERY_STRING
			// "multipart/form-data; boundary=...": uses boundaries to separate each form field
			contentType = getenv("CONTENT_TYPE");
			contentLength = atol(getenv("CONTENT_LENGTH"));
			if ( (contentType != 0) && (contentLength != 0) ) 
			{
				// CONTENT_TYPE and CONTENT_LENGTH were set and CONTENT_LENGTH>0
				if (contentLength <= MAX_CONTENT_LENGTH) 
				{
					// contentLength <= MAX_CONTENT_LENGTH
					// process stdin with a single buffer
					// create bufferDataIn
					bufferDataIn = new char[(contentLength+1)];
					if(!bufferDataIn)  
					{
						htmlError("VBMcgi internal error: Could not alloc memory !");
						return (1);
					}

					// read the whole contents of fields and its contents
					// read data from SDTIN and put it in bufferDataIn
					cin.read( bufferDataIn , contentLength );
					bufferDataIn[contentLength]='\0';
					
					// output error if size of data if not equal to contentLength 
					if (cin.gcount() != contentLength) 
					{
						// TODO undo that debbuging mess
						VBString errorMessage, temp;
						errorMessage = "VBMcgi Error: I/O error reading url encoded string. Expected: ";
						temp = contentLength; // convert to string
						errorMessage += temp;
						errorMessage += " ; Obtained: ";
						temp = cin.gcount(); // convert to string
						errorMessage += temp;
						htmlError (errorMessage);

						// delete buffer and return
						if (bufferDataIn)
						{
							delete [] bufferDataIn;
							bufferDataIn=NULL;
						}
						return(1); // return error and stop program 
					}

					VBString aux;
					if	(contentType == "application/x-www-form-urlencoded") 
					{
					// if	(!strcmp(contentType,"application/x-www-form-urlencoded")) {
						// CONTENT_TYPE = "application/x-www-form-urlencoded"
						queryStringStr = bufferDataIn;
					} 
					else 
					{
						aux = contentType.strInside(0,18);
						if (aux == "multipart/form-data") 
						{
						// CONTENT_TYPE = "multipart/form-data; boundary=..."
						char *boundary, *position, *headerEnd;
						boundary = strstr(contentType, "boundary=");
						boundary += 9;
						size_t dataLength, boundaryLength = strlen(boundary);
						char *fieldName, *fieldContents;
						if ((position=strstr(bufferDataIn, boundary))) {
							// first boundary found
							do {
								if (strncmp(position+boundaryLength, "--", 2)) {
									// not the ending boundary
									// look for header end
									if ((headerEnd=strstr(position+boundaryLength, "\r\n\r\n"))) 
									{
										// header end found
										headerEnd[0]='\0';
									} 
									else 
									{
										// header end not found -> error
										error=true;
										break;
									}

									// ignoring "Content-Disposition: " field
									if ((position=strstr(position+boundaryLength, "name=\""))) {
										// "name=" found
										fieldName=position+6;
										fieldName[dataLength=strcspn(fieldName,"\"")]='\0';
										if ((position=strstr(fieldName+dataLength+1, "filename=\""))) {
											// "filename=" found
											// 'fieldContents' gets filename
											fieldContents=position+10;
											fieldContents[dataLength=strcspn(fieldContents,"\"")]='\0';
											// save file contents
											// TODO save Content-Type somewhere and add saveDirectory option
											position=headerEnd+4;
											// look for next boundary assuming it might be a binary stream
											do {
												position=(char *)memchr((void *)(position+1), '-', contentLength-(position-bufferDataIn));
											} while (strncmp(position+2, boundary, boundaryLength) && position);
											if (position) {
												// next boundary found
												// check for empty field
												if (fieldContents[0]!='\0') {
													ofstream upFile;
													upFile.open(m_uploadDir+fieldContents, ios::out | ios::binary);
													upFile.write(headerEnd+4, position-headerEnd-6);
													upFile.close();
												}
												position+=2;
											} 
											else 
											{
												// next boundary not found -> error
												error=true;
												break;
											}

										} 
										else 
										{
											// "filename=" not found
											// it is a tradicional data field
											if ((position=strstr(headerEnd+4, boundary))) {
												// NULL-terminate the data and make 'fieldContents' point to it
												position[-4]='\0';
												fieldContents=headerEnd+4;
											} 
											else 
											{
												// next boundary not found -> error
												error=true;
												break;
											}
										}

										// add pair 'fieldName'='fieldContents' to list
										// add the pair (field_name, field_contents)
										// to the variable that is used by getVarContent
										VBMcgi_xchgNode xchgNodeLocal(fieldName,fieldContents);
										m_xchgListGVC.add(xchgNodeLocal);

										// if asked to, also gives "addBySource" to this, so the
										// "call function feature" will work on the same string pairs
										if (copyToGetVarContents) {
											addBySource(fieldName,fieldContents);
										}

									} 
									else 
									{
										// "name=" not found -> error
										error=true;
										break;
									}

								} 
								else 
								{
									// ending boundary found
									break;
								}

							} while (!error);// for each boundary

						} 
						else 
						{
							// first boundary not found
							error=true;
						}

						if (bufferDataIn)
						{
							delete [] bufferDataIn;
							bufferDataIn=NULL;
						}
						if (error) {
							htmlError ("VBMcgi Error: error decoding form data.");

							return(1);

						}

					} 

					else 
					{
						htmlError("VBMcgi Error: content type not supported");
						if (bufferDataIn)
						{
							delete [] bufferDataIn;
							bufferDataIn=NULL;
						}
						return(1); // return error and stop program 
					}
				} 
				else 
				{
					// contentLength > MAX_CONTENT_LENGTH
					// process stdin as it comes
					// not implemented yet -> error
					htmlError("VBMcgi error: CONTENT_LENGTH too big.");
					return(1); // return error and stop program
				}
			} 
			else 
			{
				// CONTENT_TYPE or CONTENT_LENGTH not set
				htmlError("VBMcgi error: CONTENT_TYPE and/or CONTENT_LENGTH not set or incorrectly set.");
				return(1); // return error and stop program
			}
			break;
#endif 
// end of POST code that suports http upload

		} // case POST



		default: 
		{
			htmlError ("VBMcgi Error: Method must be either post or get");
			return(1); // return error and stop program 
		};

	} // end switch method


#ifdef POST23
	if (bufferDataIn) { delete bufferDataIn; }
	if (bufferDataOut) { delete bufferDataOut; }
#endif

	//////////////////////////////////////////////////////////////////
	// at this point, either by post or get method, the queryStringStr 
	// has the data from the html form
	//////////////////////////////////////////////////////////////////

	decodeQueryString(queryStringStr,copyToGetVarContents);

	return 0; // no error
} // end method


void br::com::sbVB::VBMcgi::VBMcgi::decodeQueryString
(const char * queryString,bool copyToGetVarContents)
{
	VBString queryStringStr = queryString;
	decodeQueryStringVBString(queryStringStr,copyToGetVarContents);
}


// decode a given queryString
void br::com::sbVB::VBMcgi::VBMcgi::decodeQueryStringVBString
(VBString & queryStringStr,bool copyToGetVarContents)
{
	// http://.../mycgi.cgi?field_1=content_1&field_2=content_2

	if (!queryStringStr.IsEmpty()) 
	{
		// decode query string and addByCode field_name paired with field_contents
		int tokCount = 0;
		VBString field;
		do {
			// field="field_1=content_1"
			field = queryStringStr.strtok('&',tokCount);
			// increment tok count, so that next time the field will
			// get the next pair of field_name and field_contents
			tokCount++;
			// cout << "DEBUG: field=" << field << endl;

			bool hasEqualChar = field.existSubstring("=");
			if (hasEqualChar) {
				VBString field_name = field.strtok('=',0);
				VBString field_contents = field.strtok('=',1);
				field_name.escapeSequenceReverse();
				field_contents.escapeSequenceReverse();

				// add the pair field_name, field_contents to the variable that is used by getVarContent
				VBMcgi_xchgNode xchgNodeLocal(field_name,field_contents);
				m_xchgListGVC.add(xchgNodeLocal);

				// if asked to, also gives "addBySource" to this, so the
				// "call function feature" will work on the same string pairs
				if (copyToGetVarContents) {
					addBySource(field_name,field_contents);
				}
			}
		} while (field.length() > 0);
	}
}




#if 0

// class constructor

VBMcgi_htmlCookie::VBMcgi_htmlCookie() {

	

	time_t ltime;

	struct tm *today;

	

	m_csecure = false;

	today=NULL; 

	

	time(&ltime);

	today = localtime (&ltime);

	char current_time[300];

	strftime (current_time, 30, "%a, %d-%b-%Y %H:%M:%S GMT", today);

	m_current_time = current_time;

	m_cexpires = m_current_time;



} 

#endif



void br::com::sbVB::VBMcgi::VBMcgi::setCookieNameValue(const char *cookieName, const char *cookieValue) 

{

	m_cnamevalue = cookieName;

	

	// complete string with = and value 

	m_cnamevalue += "=";

	m_cnamevalue += cookieValue;

	

}



void br::com::sbVB::VBMcgi::VBMcgi::setCookieField(const char *cookieField, const char *cookieValue) {

	

	// check what field is to set, and assoc value to this



	if (VBString(cookieField) == "domain")

		m_cdomain = cookieValue;



	if (VBString(cookieField) == "path")

		m_cpath = cookieValue;

	

	if (VBString(cookieField) == "secure")

		m_csecure=true;

	

} 



bool br::com::sbVB::VBMcgi::VBMcgi::setCookieExpires (const char *period, int value) 

{

	// get system time

	time_t now, result;

	time (&now);

	tm when;

	when = *localtime (&now);



	// calculate date to expire cookie, adding value to current date

	if (value) 

	{

		if(VBString(period)=="year") when.tm_year += value;

		if(VBString(period)=="day") when.tm_mday += value;

		if(VBString(period)=="hour") when.tm_hour += value;

		if(VBString(period)=="minute") when.tm_min += value;



		// adjust structure to real time/date format 

		if ((result = mktime(&when)) != (time_t)-1) {

			when = *localtime(&result);

			char cexpires[300];

			strftime (cexpires, 30, "%a, %d-%b-%Y %H:%M:%S GMT", &when);

			m_cexpires = cexpires;

		}

		else {

			cout << "VBMcgi Error: Some error in mktime function" << endl;

			return(1);

		}

	}

	else

	{

		m_cexpires = period;

	}



	if(VBString(period)==VBString("now")) m_cexpires = m_current_time;

	if(VBString(period)==VBString("end-of-session")) m_cexpires =  "";



	return(0);

}



void br::com::sbVB::VBMcgi::VBMcgi::sendCookie() 

{

	if (!m_cnamevalue.IsEmpty()) // if the cookie has a name

	{

		cout << "Set-Cookie: " << m_cnamevalue ;

		cout << ";expires=" << m_cexpires; // inconditionally set expires

		if (!m_cpath.IsEmpty())    cout << ";path=" << m_cpath ;

		if (!m_cdomain.IsEmpty())  cout << ";domain=" << m_cdomain ;

		if (m_csecure)			   cout << ";secure";

	}

	cout << endl; // close this cookie.

}

VBString br::com::sbVB::VBMcgi::VBMcgi::getCookieValueFromHttpCookie
  (const char *http_cookie,const char *cookieName)
{
	VBString http_cookie_str,oneCookie,ret;
	VBString before;

	// tmpString = "cookieName=cookieValue;cookieName2=cookieValue2;cookieName3=cookieValue3";
	http_cookie_str = http_cookie;

	bool stop = false;
	unsigned i=0;
	while (!stop)
	{
		oneCookie = http_cookie_str.strtok(';',i);
		before = oneCookie.beforeFind("=");

		// trim leading spaces
		if (before[0] == ' ')
			before = before.afterFind(" "); 

		if (before==cookieName) 
		{
			ret = oneCookie.afterFind("=");
			return ret;
		}
		i++;
		stop = oneCookie.IsEmpty();
	}
	return ret;
}

VBString br::com::sbVB::VBMcgi::VBMcgi::getCookieValue(const char *cookieName) 
{
	VBString ret;
	char *http_cookie = getenv("HTTP_COOKIE");

	// if getenv(HTTP_COOKIE) returns something valid
	if (http_cookie) 
		ret = getCookieValueFromHttpCookie(http_cookie,cookieName);

	// if there's no cookie with given name, or getenv(HTTP_COOKIE) returns nothing, return ""
	return ret;
}


bool br::com::sbVB::VBMcgi::VBMcgi::existCookie (const char *cookieName) 
{
	VBString cookieVal = getCookieValue(cookieName);
	return (cookieVal != VBString(""));
}





// check if functionName exist in linked list of function

// if true, run relative function

bool br::com::sbVB::VBMcgi::VBMcgi::runFunction (const char *functionName) 

{

	bool ret = false;

	funcNode *ptrTemp;

	// p2 is to be used for function. This must exist even if not used.

	// scan the list of funcData to search for function names that match

	for (ptrTemp = m_funcList.GetFirst() ; ptrTemp ; ptrTemp = m_funcList.GetNext()) 

	{

		if (ptrTemp->m_funName == VBString(functionName)) 

		{

			ptrTemp->m_funPtr(*this, ptrTemp->m_pToUserClass); // run function

			ret = true;

			break; // break the search in the list

		}

	}

	return ret;

}





bool br::com::sbVB::VBMcgi::VBMcgi::insertLine (br::com::sbVB::VBMcgi::VBMcgi & cgi,

			const char * fileName,const char * reservedString, 

			const char * inputString, int insMode=0) 

{

	VBString buffer;

	bool changed = 0;



	ifstream myFile(fileName);

	if (!myFile) {

		cgi.httpCompleteHeader(); 

		cout << "File " << fileName << " not open for read" << endl;

		exit(1);

	}



        time_t ltime;

        time (&ltime); // get time

        struct tm *t = localtime (&ltime); // convert to tm struct





	VBString fileNameAux = fileName;

	fileNameAux += t->tm_min;

	fileNameAux += t->tm_sec;



	ofstream fileAux(fileNameAux);

	if (!fileAux) {

		cgi.httpCompleteHeader(); 

		cout << "File " << fileNameAux << " not open for write" << endl;

		exit(1);

	}

	

	bool notEof = true;



	while (notEof) {

		myFile >> buffer;



		bool found = buffer==VBString(reservedString);

		if (found) {

			changed = 1;

			switch (insMode) {

			case 0:

				fileAux << inputString << endl;

				fileAux << buffer << endl;

				break;

			case 1:

				fileAux << buffer << endl;

				fileAux << inputString << endl;

				break;

			default:

				cout << "Error with insMode" << endl;

				exit(1);

			};

		}

		else {

			fileAux << buffer << endl;

		}

		notEof = !myFile.eof();

	}



	myFile.close();

	fileAux.close();

	remove(fileName);

	rename(fileNameAux,fileName);



	return changed;

}



// redirects browser to szUrl

void br::com::sbVB::VBMcgi::VBMcgi::redirect(const char *szUrl) {

        

	// status 301 is needed to make the client browser see correctly

        // the address field

        cout << "status: 301" << endl;



        // 2 endl's are needed, otherwise it doesn't work !

        cout << "location: " << szUrl << endl << endl;



}



// send to stdout ONLY HTML Header 

void br::com::sbVB::VBMcgi::VBMcgi::httpHeader(const char *headerType) {
	// if HTML header not have been send, do it
	if (!m_static_haveHeader) {
		VBString tmpString ("Content-type:");
		tmpString = tmpString + headerType + "\n";
		*m_pcout << tmpString;
		m_static_haveHeader = true;
	}
} 


// send to stdout HTML Header AND page Title 
void br::com::sbVB::VBMcgi::VBMcgi::httpHeader(const char *headerType, 
											   const char *pageTitle) 
{
	// if HTML header not have been send, do it
	if (!m_static_haveHeader) {
		VBString tmpString ("Content-type:");
		tmpString = tmpString + headerType + " \n\n<html>\n<head>\n";
		tmpString = tmpString + "<title>" + pageTitle + "</title>\n</head>\n";
		*m_pcout << (tmpString);
	}
}



// send to stdout HTML header and text error

void br::com::sbVB::VBMcgi::VBMcgi::htmlError(const char* errorMessage) 
{
	httpHeader("text/html","VBMcgi Error");
	cout << "<html><body bgcolor=\"#FFFFFF\"><p align=\"center\">" << endl;
	cout << "<a href=\"http://sourceforge.net/projects/vblib\">VBMcgi</a> error: <br>" << errorMessage << "<br>" << endl; 
	cout << "</p></body></html>" << endl;
	exit(0);
} // end htmlHeader(char* errorCode)

 
